package top.lixunda.ecommerce.server.user.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import top.lixunda.commerce.common.core.exception.AppException;
import top.lixunda.common.utils.ObjectValidators;
import top.lixunda.common.utils.ServletRequestUtils;
import top.lixunda.ecommerce.server.business.BaseServiceImpl;
import top.lixunda.ecommerce.server.business.api.entity.BusinessConstants;
import top.lixunda.ecommerce.server.business.api.entity.cache.CacheUser;
import top.lixunda.ecommerce.server.user.api.entity.db.*;
import top.lixunda.ecommerce.server.user.api.entity.dto.UserInfoChgDTO;
import top.lixunda.ecommerce.server.user.dto.BindingAccountDTO;
import top.lixunda.ecommerce.server.user.dto.InsertUserDTO;
import top.lixunda.ecommerce.server.user.mapper.AccountMapper;
import top.lixunda.ecommerce.server.user.service.*;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.time.LocalDateTime;
import java.util.List;

/**
 * @author Xumda
 * @version time:2019/11/20 9:47
 */
@SuppressWarnings({"FieldCanBeLocal", "unused"})
@Slf4j
@Service
public class UserServiceImplImpl extends BaseServiceImpl<AccountMapper, AccountDO>
        implements IUserService {

    private final HttpServletRequest request;

    private final HttpServletResponse response;

    private final ILogLoginService logLoginService;

    private final AccountMapper accountMapper;

    private final IAccountBindingService accountBindingService;

    private final IUserInfoService userInfoService;

    /**
     * 密码错误锁定次数
     */
    private static final int MAX_PASSWORD_ERROR = 3;

    /**
     * 密码锁定时间，单位：小时
     */
    private static final int LOCK_TIME = 24;

    @SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
    @Autowired
    public UserServiceImplImpl(HttpServletRequest request,
                               HttpServletResponse response,
                               ILogLoginService logLoginService,
                               AccountMapper accountMapper,
                               IAccountBindingService accountBindingService,
                               IUserInfoService userInfoService) {
        this.request = request;
        this.response = response;
        this.logLoginService = logLoginService;
        this.accountMapper = accountMapper;
        this.accountBindingService = accountBindingService;
        this.userInfoService = userInfoService;
    }

    /**
     * 判断是否能够进行登录，即当前账户是否被锁定
     *
     * @param userDetail 用户信息
     * @return 可以进行登录返回null，否则返回不允许登陆的原因
     */
    @Override
    public String isLock(UserDetailDO userDetail) {
        IPage<LogLoginDO> page = logLoginService.page(new Page<>(0, MAX_PASSWORD_ERROR),
                new LambdaQueryWrapper<LogLoginDO>()
                        .orderByDesc(LogLoginDO::getTime)
                        .eq(LogLoginDO::getAccountId, userDetail.getAccount())
                        .ge(LogLoginDO::getTime, LocalDateTime.now().minusHours(LOCK_TIME)));
        if (page != null) {
            List<LogLoginDO> logLoginList = page.getRecords();
            if (log.isDebugEnabled()) {
                for (LogLoginDO l : logLoginList) {
                    log.debug(l.toString());
                }
            }
            if (ObjectValidators.isNotEmpty(logLoginList) && logLoginList.size() >= MAX_PASSWORD_ERROR) {
                for (LogLoginDO l : logLoginList) {
                    if (l.loginSuccess()) {
                        return null;
                    }
                }
                //三次全部失败
                return "账户锁定中，解锁时间：" + logLoginList.get(0).getTime().plusHours(LOCK_TIME);
            }
        }
        return null;
    }

    @Override
    public AccountBindingDO getBinding(Integer bindingType, String bindingAccount) {
        if (ObjectValidators.isEmpty(bindingType) || ObjectValidators.isEmpty(bindingAccount)) {
            throw new RuntimeException("绑定类型和绑定值不能为空");
        }
        log.debug("获取数据");
        AccountBindingDO binding = accountBindingService.getOne(
                new LambdaQueryWrapper<AccountBindingDO>()
                        .eq(AccountBindingDO::getBindingTypeId, bindingType)
                        .eq(AccountBindingDO::getBindAccount, bindingAccount)
                        .eq(AccountBindingDO::getUseable, AccountBindingDO.USEABLE_TRUE));
        log.debug("获取数据 {}", binding);
        return binding != null && binding.getAccountId() != null && binding.getAccountId() > 0 ? binding : null;
    }

    @Override
    public void insertLoginLog(UserDetailDO userDetail, boolean success) {
        LogLoginDO log = new LogLoginDO();
        log.setAccountId(userDetail.getAccount());
        log.setBindingTypeId(userDetail.getBindingTypeId());
        log.setSuccess(success ? LogLoginDO.SUCCESS : LogLoginDO.NOT_SUCCESS);
        log.setCity("");
        String ip = ServletRequestUtils.getIpAddress(request);
        log.setIpAddr(ObjectValidators.isIpAddr(ip) ? ip : "");
        log.setPhysicalAddr("");
        log.setRemark("");
        log.setTime(LocalDateTime.now());
        log.setVerifyType(BusinessConstants.USER_VERIFY_TYPE_PASS);
        logLoginService.save(log);
    }

    /**
     * 插入一个新用户, 这里的用户是普通登录用户
     *
     * @param insertUserDTO 包含新建用户需要的数据
     * @return 插入的用户数据
     */
    @Override
    public AccountDO insertNewUser(InsertUserDTO insertUserDTO) {
        // 1.插入系统账户
        AccountDO account = new AccountDO();
        account.setPassword(insertUserDTO.getPassword());
        account.setToken("");
        account.setTokenTime(LocalDateTime.now());
        account.setPassHintAns("");
        account.setPassHint("");
        LocalDateTime localDateTime = LocalDateTime.now();
        account.setCreateTime(localDateTime);
        account.setUpdateTime(localDateTime);
        account.setUserType(BusinessConstants.USER_TYPE_USER);
        account.setForbidden(BusinessConstants.FORBIDDEN_NORMAL);
        account.setRemark("");
        log.debug("插入account {}", account);
        int success = accountMapper.insert(account);
        if (success <= 0) {
            throw new AppException("插入用户数据失败");
        }
        log.debug("插入成功     {}", account);
        // 2.绑定注册账户
        bindingAccount(new BindingAccountDTO(account.getAccount(), insertUserDTO.getUsername(), insertUserDTO.getType()));
        // 3.初始化基本信息，昵称
        UserInfoChgDTO userInfoChgDTO = new UserInfoChgDTO();
        String value = insertUserDTO.getUsername();
        switch (insertUserDTO.getType()) {
            case BindingTypeDO.BINDING_TYPE_EMAIL:
                userInfoChgDTO.setEmail(value);
                break;
            case BindingTypeDO.BINDING_TYPE_ID_CARD:
                userInfoChgDTO.setIdNumber(value);
                break;
            case BindingTypeDO.BINDING_TYPE_PHONE:
                userInfoChgDTO.setPhone(value);
                break;
            case BindingTypeDO.BINDING_TYPE_QQ:
                userInfoChgDTO.setQq(value);
                break;
            case BindingTypeDO.BINDING_TYPE_WE_CHAT:
                userInfoChgDTO.setWeChat(value);
                break;
            default:
        }
        insertUserInfo(account.getAccount(), userInfoChgDTO);
        return account;
    }

    /**
     * 绑定三方账户
     *
     * @param bindingAccountDTO 绑定账户DTO
     * @return 插入的binding数据
     */
    @Override
    public AccountBindingDO bindingAccount(BindingAccountDTO bindingAccountDTO) {
        AccountBindingDO binding = new AccountBindingDO();
        binding.setAccountId(bindingAccountDTO.getSysAccount());
        binding.setBindAccount(bindingAccountDTO.getBindAccount());
        binding.setBindingTypeId(bindingAccountDTO.getType());
        binding.setTime(LocalDateTime.now());
        binding.setUseable(AccountBindingDO.USEABLE_TRUE);
        log.debug("插入AccountBinding {}", binding);
        boolean success = accountBindingService.save(binding);
        if (!success) {
            throw getAppException("绑定账户失败");
        }
        log.debug("插入成功            {}", binding);
        return binding;
    }

    /**
     * 更新当前登陆用户基本信息
     *
     * @param userInfoChgDTO 修改用户信息DTO
     * @return 用户信息
     */
    @Override
    public UserInfoDO updateUserInfo(UserInfoChgDTO userInfoChgDTO) {
        CacheUser detail = currentUserCacheNotNull();
        UserInfoDO userInfo = userInfoChgDTO.toUserInfo();
        detail.setAccount(detail.getAccount());
        boolean success = userInfoService.updateById(userInfo);
        if (success) {
            return userInfo;
        } else {
            throw new AppException("更改用户信息失败");
        }
    }

    /**
     * 插入新的用户信息
     *
     * @param account        系统账号id
     * @param userInfoChgDTO 账号信息
     * @return UserInfoDO
     */
    @Override
    public UserInfoDO insertUserInfo(Integer account, UserInfoChgDTO userInfoChgDTO) {
        UserInfoDO userInfo = userInfoChgDTO.toUserInfo();
        userInfo.setAccountId(account);
        boolean success = userInfoService.save(userInfo);
        if (!success) {
            throw getAppException("插入用户信息失败");
        }
        return userInfo;
    }
}
